import React, { StrictMode, useState, useRef, useImperativeHandle, useContext } from 'react'
const boxStyle = {
  margin: '10px',
  border: '1px solid #ccc',
  borderRadius: '10px',
  padding: '10px',
}
// +++++++++++++++++++++++++++++ 总结 +++++++++++++++++++++++++++++++++
// Context 可以让我们无须明确地传遍每一个组件，就能将值深入传递进组件树。

// 为当前的 theme 创建一个 context（“light”为默认值）。
const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};
const ThemeContext = React.createContext(themes.dark);
// context 对象接受一个名为 displayName 的 property，类型为字符串。R
// React DevTools 使用该字符串来确定 context 要显示的内容。
ThemeContext.displayName = 'ThemeContext';
// 用户登录 context
const UserContext = React.createContext({
  password: 'hahacc',
});
UserContext.displayName = 'UserContext';

// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深，任何组件都能读取这个值。
export class ConTextTemplate extends React.Component {
  constructor(props) {
    super(props)
    // 当 Provider 的 value 值发生变化时，它内部的所有消费组件都会重新渲染。
    // 当Provider更新时Consumer, 不受制于父节点及自身的shouldComponentUpdate 函数限制
    this.state = {
      str: '单层使用conText',
      theme: themes.light,
      name: 'Guest',
      userProvide: { password: '232323' },
    }

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };

    this.handleChange = (event) => {
      this.setState({ name: event.target.value });
    }
  }
  componentDidMount() {
    console.log('conTextTemplate...', ThemeContext)
  }
  render() {
    const sentProvider = {
      theme: this.state.theme,
      name: this.state.name,
      toggleTheme: this.toggleTheme,
    }
    return (
      <div style={boxStyle}>
        <p>conText的常规使用方式</p>
        <div>
          <button style={{ marginBottom: '10px', background: this.state.theme.background }} onClick={this.toggleTheme} >改变主题</button>
          <div>
            改变用户名称<input type="input" value={this.state.name} onChange={this.handleChange} />
          </div>
        </div>
        {/* ThemeContext.Provider value 会覆盖默认值, 即使是不同类型, 
          将 undefined 传递给 Provider 的 value 时，消费组件的 defaultValue 不会生效
        */}
        {/* <ThemeContext.Provider value={'dark'}> */}
        <ThemeContext.Provider value={sentProvider}>
          <UserContext.Provider value={this.state.userProvide}>
            <Toolbar onClick={this.toggleTheme} />
            <Content />
            <Layout />
            <FunLayout />
          </UserContext.Provider>
        </ThemeContext.Provider>
      </div>
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
// {...props}通过这种方式可以将组件上绑定的方法及属性穿透传递
function Toolbar(props) {
  return (
    <div style={boxStyle}>
      <p>指定static  contextType,后this.context接收数据</p>
      <ThemedButton {...props} />
    </div>
  );
}

// +++++++++++++++++++++++++++++ 使用static contextType指定接收context +++++++++++++++++++++++++++++++++
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider，然后使用它的值。
class ThemedButton extends React.Component {
  // static contextType = ThemeContext;
  componentDidMount() {
    console.log('ThemedButton...contextType...', ThemeContext)
  }
  render() {
    return (
      <React.Fragment>
        <p>context引入的用户名称:{this.context.name}</p>
        <button disabled={true} style={{ background: this.context.theme.background, color: '#ccc' }} >主题颜色</button>
        <button {...this.props}>调用爷爷...props穿透传入的方法</button>
      </React.Fragment>
    )
  }
}
// 在该class中接收单个context, 需通过contextType指定对应context
ThemedButton.contextType = ThemeContext;

// +++++++++++++++++++++++++++++ 通过context.Consumer接收数据 +++++++++++++++++++++++++++++++++
function Content() {
  return (
    <div style={boxStyle}>
      <p>context.consumer接收数据</p>
      <ThemeTogglerButton />
    </div>
  );
}

// 在class组件中, 通过作为Context.Consumer的作用域插槽(render.props单个使用子组件的写法), 可以接收provide提供数据
function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值，
  // 它也从 context 中获取到一个 toggleTheme 函数
  return (
    <ThemeContext.Consumer>
      {({ theme, toggleTheme }) => (
        <button
          onClick={toggleTheme}
          style={{ backgroundColor: theme.background }}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

// +++++++++++++++++++++++++++++ 通过context.Consumer接收多个context数据 +++++++++++++++++++++++++++++++++
function Layout() {
  return (
    <div style={boxStyle}>
      <p>context.Consumer接收多个context数据 </p>
      <Content2 />
    </div>
  );
}

function Content2() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <>
              <p>接收ThemeContext数据:{theme.theme.background}</p>
              <p>接收UserContext数据:{user.password}</p>
            </>
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

// +++++++++++++++++++++++++++++ 函数组件useContext +++++++++++++++++++++++++++++++++
function FunLayout() {
  return (
    <div style={boxStyle}>
      <p>函数组件使用多个context数据 </p>
      <Content3 />
    </div>
  );
}

function Content3() {
  const theme = useContext(ThemeContext);
  const user = useContext(UserContext);

  return (
    <>
      <p>接收ThemeContext数据:{theme.theme.background}</p>
      <p>接收UserContext数据:{user.password}</p>
    </>
  )
}